/******************************************************************************
        (c) COPYRIGHT 2017-  by D.C.P  Co.,Ltd
                          All rights reserved.
@file: TEST.h
@brief:对于链表模块的结构体定义
@author   D.C.P 			                                                 
@version  0.0.0.1                                                               
@date     2017/03/29
-------------------------------------------------------------------------------
  Change History :                                                              
                 | <Date>      | <Version> | <Author> | <Description>                     
-------------------------------------------------------------------------------
  @todo          | 2017/03/29  | 0.0.0.1   | D.C.P    | 创建文件             
******************************************************************************/
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

#ifndef __INC_TEST_H__
#define __INC_TEST_H__

#if !defined( OS_MAC)
        #include <mcheck.h>//内存泄漏检查头文件
#endif
/**  @brief 成功返回 */
#define TEST_SUCC FUNC_SUCC
/**  @brief 失败返回 */
#define TEST_FAIL FUNC_FAIL
/**  @brief 失败空指针 */
#define TEST_NULL FUNC_NULL
/**  @brief 监控内存文件 */
#define MONITOR_MALLOC_FILE "./stdout"
/**  @brief 装载和卸载函数指针 */
typedef void(*data_handler_t)(void);

/**  @brief 套件结构体 */
struct suite {
        string_t SuiteName;           /**< 套件名字*/
        u8_t SuiteSum;                /**< 测试实例数*/
        u8_t SuiteErrSum;             /**< 测试实例出错数*/
        u32_t SuiteTime;              /**< 测试用时 精度毫秒*/
        data_handler_t SuiteSetup;    /**< case装载数据函数*/
        data_handler_t SuiteTeardown; /**< case卸载数据函数*/
};

/**  @brief 实例结构体 */
struct Case {
    string_t CaseName;    /**< 实例名字*/
    u32_t Line;           /**< 出错位置所在的行数 */
    string_t File;        /**< 出错位置所在的文件 */
    string_t Function;    /**< 出错位置所在的函数 */
    u32_t CaseTime;       /**< 测试用时 精度毫秒  */
    u32_t PrintLen;       /**< 需要打印的内容的长度*/
    char ErrText[512];    /**< 错误内容*/
    char PrintText[1024]; /**< 需要打印的内容*/
};

/******************************************************************
*   Name：私有函数接口
*   Desc：内部调用的一些函数
******************************************************************/
/**
* @brief 初始化测试套件
* @param [in] testSuite   测试套件
*/
u32_t 
_test_init_suite(struct suite *testSuite);
/**
* @brief 开始一个套件测试
* @param [in] testSuite 套件结构体
* @attention 当定义了TEST_FILE,就将测试内容写入文件
*/
u32_t 
_test_suite_start(struct suite *testSuite);
/**
* @brief 结束一个套件测试
* @param [in] testSuite 套件结构体
* @attention 当定义了TEST_FILE,就将测试内容写入文件
*/
u32_t 
_test_suite_end(struct suite *testSuite);
/**
* @brief 初始化套件的测试实例
* @param [in] testCase   实例
*/
u32_t 
_test_init_case(struct Case *testCase);
/**
* @brief 处理测试的实例结果
* @param [in] testCase  实例结构体
* @param [in] desc      实例描述
* @param [in] result    实例测试结果
* @attention 当定义了TEST_FILE,就将测试内容写入文件
*/
u32_t 
_test_show_case_result(struct suite *testSuite, struct Case *testCase, string_t desc, u32_t result);
/******************************************************************
*   Name：私有接口宏 
*   Desc：内部调用的一些宏，主要是获得异常函数调用堆栈
******************************************************************/
/**
* @brief 符号条件判断，失败时候，退出实例测试
* @param [in] func   出错函数名
* @param [in] line   出错行数
* @param [in] file   出错文件
* @param [in] value1   参与条件判断的值1
* @param [in] value2   参与条件判断的值2
* @param [in] condition   判断解决
* @param [in] desc   错误描述
*/
#define RETURN_VAL(func, line, file, value1, value2, condition, desc) \
do{                                               \
        if (!(condition))                         \
        {                                         \
            snprintf(testCase->ErrText,512,"[ ERR    ]预期值(expected value)：%d\n[ ERR    ]结果值(result value) %d\n[ ERR    ]预期结果(expected result)：%s\n", value1, value2, #desc);\
            testCase->Line = (u32_t )line;        \
            testCase->File = (char *)file;        \
            testCase->Function = (char *)func;    \
            return TEST_FAIL;                     \
        }                                         \
}while(0)
/**
* @brief 字符串条件判断，失败时候，退出实例测试
* @param [in] func   出错函数名
* @param [in] line   出错行数
* @param [in] file   出错文件
* @param [in] str   期望结果
* @param [in] result   返回字符串结果
* @param [in] condition   判断解决
* @param [in] desc   错误描述
*/
#define RETURN_STR(func, line, file, str, result, condition, desc) \
do{                                               \
        if (!(condition))                         \
        {                                         \
            snprintf(testCase->ErrText,512,"[ ERR    ]预期字符串(expected string)：%s\n[ ERR    ]结果字符串(result string)： %s\n[ ERR    ]预期结果(expected result)： %s", str, result, #desc);\
            testCase->Line = (u32_t )line;        \
            testCase->File = (char *)file;        \
            testCase->Function = (char *)func;    \
            return TEST_FAIL;                     \
        }                                         \
}while(0)
/**
* @brief 定义需要测试的测试函数开始位置 并启动套件计时器
* @param [in] name   测试套件名
* @param [in] setup  装载case初始化函数
* @param [in] name   测试套件名
*/
#define _TEST_SUITE_START(name)                \
{                                              \
                struct suite testSuite;        \
                _test_init_suite(&testSuite);  \
                testSuite.SuiteName = #name;   \
                _test_suite_start(&testSuite); \
                clock_t start = clock();

/**
* @brief 定义需要测试的测试函数结束位置
*/
#define _TEST_SUITE_END                      \
    testSuite.SuiteTime = (clock() - start); \
    _test_suite_end(&testSuite);             \
}
/**
* @brief 注册suite下的case的数据装载和卸载函数
*/
#define _TEST_SUITE_REGISTER_SETUP_TEARDOWN(setup, teardown) \
do{                                                          \
        testSuite.SuiteSetup = setup;                        \
                testSuite.SuiteTeardown = teardown;          \
} while (0)
/**
* @brief 定义需要测试的测试函数开始位置
* @param [in] _testfunc   测试函数名
*/
#define _DEFINE_TEST_CODE_START(_testfunc)    \
static u32_t _testfunc(struct Case *testCase) \
{                              

/**
* @brief 定义需要测试的测试函数结束位置
*/
#define _DEFINE_TEST_CODE_END \
    return TEST_SUCC;         \
}
/**
* @brief 测试一个实例
* @param [in] _testfunc   已经定义的测试函数名
* @param [in] desc        测试实例名
* @param [in] desc        测试实例描述
*/
#define _TEST_CASE(_testfunc, name, desc)                                     \
do{                                                                           \
                struct Case testCase;                                         \
                _test_init_case(&testCase);                                   \
                testCase.CaseName = #name;                                    \
                if (NULL != testSuite.SuiteSetup)                             \
                        testSuite.SuiteSetup();                               \
                u32_t result = _testfunc(&testCase);                          \
                if (NULL != testSuite.SuiteTeardown)                          \
                        testSuite.SuiteTeardown();                            \
                _test_show_case_result(&testSuite, &testCase, #desc, result); \
}while (0)

/**
* @brief 开始测试一段代码运行时间
*/
#define _TEST_START_TIMER \
        clock_t caseStart = clock();
/**
* @brief 结束测试一段代码运行时间
*/
#define _TEST_END_TIMER \
        testCase->CaseTime = clock() - caseStart;
/**
* @brief 测试打印
*/
#define _TEST_PRINT(...) testCase->PrintLen += snprintf(testCase->PrintText+testCase->PrintLen ,1024-testCase->PrintLen,__VA_ARGS__);
/**
* @brief 定义缓冲区数据装载函数 每次调用case都会进行装载和卸载
* @param [in] _testsetup   装载函数名
*/
#define _TEST_DEFINE_CASE_SETUP(_testsetup)  static void _testsetup(void) 
/**
* @brief 定义缓冲区数据卸载函数 每次调用case都会进行装载和卸载
* @param [in] _testteardown  卸载函数名
*/
#define _TEST_DEFINE_CASE_TEARDOWN(_testteardown)  static void _testteardown(void) 
/**
* @brief 测试的case所需要的数据缓存区开始位置
* @param [in] _testcache   数据缓存区名
*/
#define _TEST_CACHE_START(_testcache) \
struct Suite##_testcache {
                              
/**
* @brief 测试的case所需要的数据缓存区结束位置
* @param [in] _testcache   数据缓存区名
*/
#define _TEST_CACHE_END(_testcache) \
}_testcache;
/**
* @brief 获取缓冲区内容
* @param [in] _testcache   缓存区名
* @param [in] data         数据
* @attention 任意的data，假如获取数组下标4的值，就_GET_CATCH_DATA( test, data[4])
*/
#define _GET_CATCH_DATA( _testcache, data) _testcache.data
/**
* @brief 设置缓冲区内容
* @param [in] _testcache   缓存区名
* @param [in] data         数据
* @param [in] value         新值
* @attention 任意的data，假如设置数组下标4的值，就_GET_CATCH_DATA( test, data[4], 1 )
*/
#define _SET_CATCH_DATA(_testcache, data, value) \
        _testcache.data = value

#endif /*__INC_TEST_H__*/

#ifdef __cplusplus
}
#endif /* __cplusplus */
